package mgodb

import (
	"jgt-oper/src/logger"
	"jgt-oper/src/model"
	"jgt-oper/src/schema"
	"jgt-oper/src/service/mongo"
	"time"

	"github.com/facebookgo/inject"
	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"github.com/pkg/errors"
)

// cAuthCode 授权码管理表名
const cAuthCode = "oper_auth_code"
const cAuthHistory = "oper_auth_history"

// AuthCode 授权码管理
type AuthCode struct {
	DB *mongo.DB
}

// Init 初始化
func (a *AuthCode) Init(g *inject.Graph, db *mongo.DB) {
	a.DB = db

	// 初始化mongo索引
	a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		c.EnsureIndex(mgo.Index{
			Key:    []string{"record_id"},
			Unique: true,
		})
		c.EnsureIndex(mgo.Index{
			Key:    []string{"code"},
			Unique: true,
		})
		c.EnsureIndexKey("deleted")
		c.EnsureIndexKey("deleted", "status")
		c.EnsureIndexKey("deleted", "code")
		return nil
	})

	a.DB.C(cAuthHistory, func(c *mgo.Collection) error {
		c.EnsureIndexKey("deleted", "code_id")
		return nil
	})

	var val model.IAuthCode = a
	g.Provide(&inject.Object{Value: val, Name: "IAuthCode"})
}

// QueryPage 查询分页数据
func (a *AuthCode) QueryPage(params schema.AuthCodeQueryParam, pageIndex, pageSize uint) (int64, []*schema.AuthCodeQueryResult, error) {
	query := bson.M{
		"deleted": 0,
	}
	if v := params.EnterpriseID; v != "" {
		query["enterprise_id"] = v
	}
	if v := params.Code; v != "" {
		query["code"] = bson.M{"$regex": v}
	}
	if v := params.CodeType; v != "" {
		query["code_type"] = v
	}
	if v := params.EffectiveMode; v != 0 {
		query["effective_mode"] = v
	}
	if v := params.Status; v != 0 {
		query["status"] = v
	}

	if start, end := params.EffectiveStart, params.EffectiveEnd; start != 0 && end != 0 {
		query["$and"] = []bson.M{
			bson.M{"effective_start": bson.M{"$gte": start}},
			bson.M{"effective_end": bson.M{"$lte": end}},
		}
	}

	if v := params.LastID; v > 0 {
		query["_id"] = bson.M{"$lt": v}
	}

	var total int64
	var items []*schema.AuthCodeQueryResult
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		n, err := c.Find(query).Count()
		if err != nil {
			return err
		}
		total = int64(n)
		if total == 0 {
			return nil
		}

		q := c.Find(query).Sort("-_id")
		if pageIndex > 0 {
			q = q.Skip(int((pageIndex - 1) * pageSize))
		}
		if pageSize > 0 {
			q = q.Limit(int(pageSize))
		}

		return q.All(&items)
	})
	if err != nil {
		return 0, nil, errors.Wrapf(err, "查询分页数据发生错误")
	}

	return total, items, nil
}

// QueryHistoryPage 查询历史分页数据
func (a *AuthCode) QueryHistoryPage(params schema.AuthHistoryQueryParam, pageIndex, pageSize uint) (int64, []*schema.AuthHistoryQueryResult, error) {
	query := bson.M{
		"deleted": 0,
	}
	if v := params.CodeID; v != "" {
		query["code_id"] = v
	}

	if v := params.LastID; v > 0 {
		query["_id"] = bson.M{"$lt": v}
	}

	if v := params.RealName; v != "" {
		query["real_name"] = bson.M{"$regex": v}
	}

	var pipeline []bson.M
	pipeline = append(pipeline, bson.M{
		"$match": query,
	})
	pipeline = append(pipeline, bson.M{
		"$lookup": bson.M{
			"from":         cAuthUser,
			"localField":   "user_id",
			"foreignField": "_id",
			"as":           "auth_users",
		},
	})

	if v := params.Tel; v != "" {
		pipeline = append(pipeline, bson.M{
			"$match": bson.M{
				"auth_users": bson.M{
					"$elemMatch": bson.M{
						"tel": bson.M{
							"$regex": v,
						},
					},
				},
			},
		})
	}

	if v := params.QueryValue; v != "" {
		pipeline = append(pipeline, bson.M{
			"$match": bson.M{
				"$or": []bson.M{
					bson.M{"real_name": bson.M{"$regex": v}},
					bson.M{
						"auth_users": bson.M{
							"$elemMatch": bson.M{
								"tel": bson.M{
									"$regex": v,
								},
							},
						},
					},
				},
			},
		})
	}

	var total int64
	var items []*schema.AuthHistoryQueryResult
	err := a.DB.C(cAuthHistory, func(c *mgo.Collection) error {
		countPipe := pipeline[:]
		countPipe = append(countPipe, bson.M{
			"$group": bson.M{
				"_id":   nil,
				"count": bson.M{"$sum": 1},
			},
		})

		var countResult struct {
			Count int64 `bson:"count"`
		}

		logger.System("").Debugf("查询认证码历史-数据条数：%v", countPipe)

		err := c.Pipe(countPipe).One(&countResult)
		if err != nil {
			if err == mgo.ErrNotFound {
				return nil
			}
			return err
		}
		total = countResult.Count
		if total == 0 {
			return nil
		}

		pipeline = append(pipeline, bson.M{
			"$sort": bson.M{"_id": -1},
		})

		if pageIndex > 0 {
			pipeline = append(pipeline, bson.M{
				"$skip": (pageIndex - 1) * pageSize,
			})
		}

		if pageSize > 0 {
			pipeline = append(pipeline, bson.M{
				"$limit": pageSize,
			})
		}

		logger.System("").Debugf("查询认证码历史：%v", pipeline)

		return c.Pipe(pipeline).All(&items)
	})
	if err != nil {
		return 0, nil, errors.Wrapf(err, "查询分页数据发生错误")
	}

	return total, items, nil
}

// Get 精确查询数据
func (a *AuthCode) Get(recordID string) (*schema.AuthCode, error) {
	var item schema.AuthCode
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		return c.Find(bson.M{"record_id": recordID}).One(&item)
	})
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, errors.Wrapf(err, "精确查询数据发生错误")
	}
	return &item, nil
}

// GetByCode 根据验证码获取数据
func (a *AuthCode) GetByCode(code string) (*schema.AuthCode, error) {
	var item schema.AuthCode
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		return c.Find(bson.M{"code": code, "deleted": 0}).One(&item)
	})
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, errors.Wrapf(err, "根据验证码获取数据发生错误")
	}
	return &item, nil
}

// CheckCode 检查验证码是否存在
func (a *AuthCode) CheckCode(code string) (bool, error) {
	var exists bool
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		n, err := c.Find(bson.M{"deleted": 0, "code": code}).Count()
		if err != nil {
			return err
		}
		exists = n > 0
		return nil
	})
	if err != nil {
		return false, errors.Wrapf(err, "检查验证码是否存在发生错误")
	}

	return exists, nil
}

// Create 创建数据
func (a *AuthCode) Create(item *schema.AuthCode) error {
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		id, err := a.DB.IncrID(cAuthCode)
		if err != nil {
			return err
		}
		item.ID = id
		return c.Insert(item)
	})
	if err != nil {
		return errors.Wrapf(err, "创建数据发生错误")
	}

	return nil
}

// Update 更新数据
func (a *AuthCode) Update(recordID string, info map[string]interface{}) error {
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		return c.Update(bson.M{"record_id": recordID}, bson.M{"$set": info})
	})
	if err != nil {
		return errors.Wrapf(err, "更新数据发生错误")
	}
	return nil
}

// Delete 删除数据
func (a *AuthCode) Delete(recordID string) error {
	info := map[string]interface{}{
		"deleted": time.Now().Unix(),
	}
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		return c.Update(bson.M{"record_id": recordID}, bson.M{"$set": info})
	})
	if err != nil {
		return errors.Wrapf(err, "删除数据发生错误")
	}
	return nil
}

// IncUsedNumber 递增使用次数
func (a *AuthCode) IncUsedNumber(recordID string) error {
	err := a.DB.C(cAuthCode, func(c *mgo.Collection) error {
		return c.Update(bson.M{"record_id": recordID}, bson.M{"$inc": bson.M{"used_number": 1}})
	})
	if err != nil {
		return errors.Wrapf(err, "递增使用次数发生错误")
	}
	return nil
}

// CreateHistory 创建历史数据
func (a *AuthCode) CreateHistory(item *schema.AuthHistory) error {
	err := a.DB.C(cAuthHistory, func(c *mgo.Collection) error {
		id, err := a.DB.IncrID(cAuthHistory)
		if err != nil {
			return err
		}
		item.ID = id
		return c.Insert(item)
	})
	if err != nil {
		return errors.Wrapf(err, "创建历史数据发生错误")
	}

	return nil
}
